/*  
     PlAr is Platform Arena: a 2D multiplayer shooting game
     Copyright (C) 2009 Ragagnin Antonio <spocchio@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package plar.ClientServer;

import java.awt.Point;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;

import plar.core.Common;
import plar.core.Game;
import plar.core.Level;
import plar.core.Sprite;
import plar.core.defaultElements.Player;
import plar.elements.Qeen;
import plar.elements.Squeak;
import plar.elements.KittyCat;

class Connect extends Thread {
	private Socket client = null;
	public Game g;
	private ObjectInputStream ois = null;
	private ObjectOutputStream oos = null;
	private ArrayList<ArrayList<Sprite>> buffer;

	public Connect() {

	}

	public Connect(Socket clientSocket,Game game) {
		g=game;
		client = clientSocket;
		
		try {
			client.setTcpNoDelay(true);
			Common.info("delay: "+client.getTcpNoDelay());
		} catch (SocketException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
		
	
		try {
			ois = new ObjectInputStream(client.getInputStream());
			oos = new ObjectOutputStream(client.getOutputStream());
			buffer = new ArrayList<ArrayList<Sprite>>();
		} catch (Exception e1) {
			try {
				// client.close();
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
			return;
		}
		this.start();
	}

	public void run() {
		Player p=null;
		ArrayList <ShownElement> screen=null;
		ArrayList<Integer> indexToSend = null;
		try {

			String a = "Welcome to " + Common.product+" Server ver:"+Common.version;
			Common.info("welcome to new user!" + a);
			oos.writeObject(new String(a));
			oos.writeObject(new Point(g.resolution));

			Common.info("welcome to new user!" + a);
			 p = new Player();

			while (true) {
				// ois = new ObjectInputStream(client.getInputStream());
				// oos = new ObjectOutputStream(client.getOutputStream());

				// Common.info("waiting 4 int :");
					//String[] data = ((String) ois.readObject()).split(" ");
				Integer data = ois.readInt();
				// Common.info("received :"+data);
		if (data==Signal.PLAYER) {
					String[] giocatore = ((String) ois.readObject()).split(" ");
					String name = giocatore[1];
					String type = giocatore[0];

					if (type.equals(".queen"))
						p = new Qeen();
					else if (type.equals(".kitty"))
						p = new KittyCat();
					else
						p = new Squeak();
					p.setProperty("user", name);
					g.addPlayer(p);
					Common.info("added player " + name + " type:" + type);
					g.sendChat(p,"On the game!");
				}
		if (data==Signal.UPDATE) {
			//		Common.info("---> U");
					screen = g.getScreen(p);
					indexToSend = new ArrayList<Integer>();
					ArrayList<Integer> indexToBeAdded = new ArrayList<Integer>();
					
					// String addS=new String();
			     	for (int i = 0; i < screen.size(); i++) {
						ShownElement e = screen.get(i);
						ArrayList <Sprite> al = e.spriteSet;
						String als = al.toString();
						boolean exist = false;
						for(int j=0;(j<buffer.size() && !exist);j++)
						{
							ArrayList <Sprite> bal = buffer.get(j);
							String bals = bal.toString();
							if(als.equals(bals))
							{
								exist = true;
						//		Common.info("found element in position "+j);
								indexToSend.add(j);
							}
						}
						
						
						if(!exist)
						{
							Common.info("adding "+als);
							buffer.add(al);
							indexToBeAdded.add(buffer.size()-1);
							indexToSend.add(buffer.size()-1);
						}
					}
			     	
			    	oos.writeInt(indexToBeAdded.size());
			    	oos.flush();
			    //	Common.info("index to add"+indexToBeAdded.size());
			     	for(int i=0;i<indexToBeAdded.size();i++)
			     	{
			     		oos.writeObject(buffer.get(indexToBeAdded.get(i)));
			     //		Common.info("sended "+buffer.get(indexToBeAdded.get(i)));
			     	}


				
				}
		
			if (data==Signal.DIRECTION) {
				int x = ois.readInt();
				int y = ois.readInt();
				double dx = (double)x;
				double dy = (double)y;
				dx/=100;
				dy/=100;
				Common.info("setting dirx"+dx+" diry"+dy+"");
				p.setProperty("direction",Math.abs(Math.abs((dx/Math.abs(dx))*0.5-0.5)));
				p.setProperty("directionx",dx);
				p.setProperty("directiony",dy);
			}
				if (data==Signal.CHAT) {
					String c = (String)ois.readObject();
					g.sendChat(p,c);
				}
				if (data==Signal.GET_MSG) {
			
					String s = g.getChat(p);
					oos.writeObject(s);
				}			
				if(data==Signal.LIFE)
				{
					int life = (Integer) p.getProperty("energy");
					oos.writeInt(life);
				}
				if (data==Signal.SCREEN) {
				//	Common.info("---> S");
				//	Common.info("written int "+(screen.size()-1));
					oos.writeInt(screen.size());
					for (int i = 0; i < screen.size(); i++) {
						ShownElement s = screen.get(i);
						// Common.info("sending sprite"+i+" s="+s.rectangle.x);

						oos.writeInt(s.rectangle.x);
						oos.writeInt(s.rectangle.y);
						oos.writeInt(s.rectangle.width);
						oos.writeInt(s.rectangle.height);
						oos.writeInt(indexToSend.get(i));
						//Common.info("sended "+s.rectangle+" "+indexToSend.get(i));
					}
					

				
				}

				if (data==Signal.POSITION) {
					oos.writeInt(p.getPosition().x);
					oos.writeInt(p.getPosition().y);
					oos.writeInt(p.getSpeed().x);
					oos.writeInt(p.getSpeed().y);

				}
				if (data==Signal.INPUT) {
				//	Common.info("I");
					String[] str = ((String) ois.readObject()).split(" ");

					ArrayList<String> premuti = new ArrayList<String>();
					for (int i = 0; i < str.length; i++) {
					//	Common.info("key " + data[i]);
						premuti.add(str[i]);

					}
					g.sendInput(p, premuti);

				}
				
			
				data = null;

				oos.flush();

				// d.data.clear();
			}

			// close streams and connections
			// ois.close();
			// oos.close();
			// client.close();

		} catch (Exception e) {
			Common.info("eccezione:" + e);

		}
		Common.info("bye bye");
		g.sendChat(p,"Exit the game!");
		
		g.level.delElement(p);
		close();
	}

	public void close()  {
	try {
		ois.close();
		oos.close();
		client.close();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
		
	}

}

public class Server extends Thread {
	public boolean active = true;
	public ServerSocket dateServer;
	public Game g;
	public Level level;
	public HashMap<String, Player> playerList;
	public int refresh = 10;
	public Point resolution;
    public ArrayList <Connect> connections;
	public Server() {
		super();
		resolution = new Point(800, 600);
		connections = new  ArrayList <Connect> ();
	}

	public void close() {
		active = false;
		for(Connect c:connections)
		{
			c.close();
		
		}
		
		try {
			dateServer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.interrupt();

	}

	public boolean listen(int port) {
		try
		{
			dateServer = new ServerSocket(port);
			System.out.println("Server listening on port " + port + ".");
			this.start();
		}catch(Exception e)
		{
			return false;
		}
		return true;
	}

	public void newGame(Level l) {
		g = new Game();
		g.setLevel(l);
		g.startGame();

		class autoupdate extends Thread {

			public void run() {

				try {
					while (active) {

						g.run();

						Thread.sleep(refresh);
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		Thread t = new autoupdate();
		t.setName("Thread principale");
		t.setPriority(10);
		System.out.println("Thread in esecuzione: " + t);

		t.start();

		Common.info("game started, refreshtime=" + refresh + " level=" + level);
	}

	public void run() {
		while (active) {
			try {
				System.out.println("Waiting for connections.");
				Socket client = dateServer.accept();
				System.out.println("Accepted a connection from: "
						+ client.getInetAddress());
				Connect c = new Connect(client,g);
				
				//c.g = g;
				connections.add(c);
			} catch (Exception e) {
			}
		}

	}
}
